package com.example.yupao_project.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.yupao_project.component.ErrorCode;
import com.example.yupao_project.component.IsAdmin;
import com.example.yupao_project.entity.Team;
import com.example.yupao_project.entity.User;
import com.example.yupao_project.entity.UserTeam;
import com.example.yupao_project.entity.enums.TeamStatusEnum;
import com.example.yupao_project.entity.infovo.TeamUserVo;
import com.example.yupao_project.entity.infovo.UserVo;
import com.example.yupao_project.entity.param.TeamJoinRe;
import com.example.yupao_project.entity.param.TeamQuery;
import com.example.yupao_project.entity.param.TeamQuitRe;
import com.example.yupao_project.entity.param.TeamUpdateRe;
import com.example.yupao_project.exception.BusinessException;
import com.example.yupao_project.mapper.TeamMapper;
import com.example.yupao_project.service.TeamService;
import com.example.yupao_project.service.UserService;
import com.example.yupao_project.service.UserTeamService;
import jakarta.annotation.Resource;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author 杰
 * @description 针对表【team(队伍)】的数据库操作Service实现
 * @createDate 2024-05-03 19:15:23
 */
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService {
    @Resource
    private UserTeamService userTeamService;
    @Resource
    private TeamMapper teamMapper;
    @Resource
    private UserService userService;
    @Resource
    private IsAdmin isAdmin;

    @Resource
    private RedissonClient redissonClient;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long addTeam(Team team, User loginUser) {
        //1. 判断参数是否为空
        if (team == null) throw new BusinessException(ErrorCode.NULL_ERROR);
        if (loginUser == null) throw new BusinessException(ErrorCode.NOT_LOGIN);
        final long userId = loginUser.getId();
        //2. 校验参数
        //2.1 判断队伍人数
        if (team.getMaxNum() == null || team.getMaxNum() <= 0 || team.getMaxNum() > 20)
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不符合要求");
        //2.2 判断队伍名
        String name = team.getName();
        if (!StringUtils.hasLength(name) || name.length() > 20)
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题不满足要求");
        //2.3 判断队伍描述
        String describe = team.getDescription();
        if (describe.length() > 512)
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长");
        //2.4 status是否公开（int）不传默认为0（公开）
        TeamStatusEnum teamStatusEnum = Optional.ofNullable(TeamStatusEnum.getEnumByValue(team.getStatus())).orElse(TeamStatusEnum.PUBLIC);
        //2.5 如果status是加密状态，那么就必须要含有密码，且密码<32
        String password = team.getPassword();
        if (TeamStatusEnum.SECRET.equals(teamStatusEnum)) {
            if (!StringUtils.hasLength(password) || password.length() > 32) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码设置不正确");
            }
        }
        //2.6 超时时间大于当前时间（房间过期）
        Date expireTime = team.getExpireTime();
        if (new Date().after(expireTime)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "超时时间 > 当前时间");
        }

        //2.7 校验用户最多创建5个队伍
        // TODO: 2024/5/14 加锁（已完成）
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long hasTeamNum = this.count(queryWrapper);
        QueryWrapper<UserTeam> qw_UserTeam = new QueryWrapper<>();
        qw_UserTeam.eq("userId",userId);
        long userTeamCount = userTeamService.count(qw_UserTeam);
        if (hasTeamNum >= 5 || userTeamCount >= 5)
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建或加入 5 个队伍");

        String addLock = "yu_pao:addTeam:" + loginUser.getId();
        RLock lock = redissonClient.getLock(addLock);
        try {
            if (lock.tryLock(0,-1,TimeUnit.MILLISECONDS)) {
                //3. 存储队伍信息
                team.setId(null);
                team.setUserId(userId);
                //3.1 插入队伍信息到队伍表
                boolean insertTeam = this.save(team);
                Long teamId = team.getId();
                if (!insertTeam) throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
                //3.2 插入用户 -> 队伍关系到关系表
                QueryWrapper<Team> queryWrapper1 = new QueryWrapper<>(team);
                Team eqteam = teamMapper.selectOne(queryWrapper1);
                UserTeam userTeam = new UserTeam();
                userTeam.setUserId(userId);
                userTeam.setTeamId(eqteam.getId());
                userTeam.setJoinTime(new Date());
                userTeam.setIsDelete(0);

                if (!userTeamService.save(userTeam))
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
                return teamId;
            }
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
        return 0L;
    }

    /**
     * 根据条件查询team
     * @param teamQuery 查询信息
     * @param loginUser 登录用户信息
     * @return List<TeamUserVo>
     */
    @Override
    public List<TeamUserVo> listTeams(TeamQuery teamQuery, User loginUser) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // 组合查询条件
        if (teamQuery != null) {
            Long id = teamQuery.getId();
            if (id != null && id > 0) {
                queryWrapper.eq("id", id);
            }

            List<Long> idList = teamQuery.getIdList();
            if (!CollectionUtils.isEmpty(idList)) {
                queryWrapper.in("id", idList);
            }
            String searchText = teamQuery.getSearchText();
            if (StringUtils.hasLength(searchText)) {
                queryWrapper.and(qw -> qw.like("name", searchText).or().like("description", searchText));
            }
            String name = teamQuery.getName();
            if (StringUtils.hasLength(name)) {
                queryWrapper.like("name", name);
            }
            String description = teamQuery.getDescription();
            if (StringUtils.hasLength(description)) {
                queryWrapper.like("description", description);
            }
            Integer maxNum = teamQuery.getMaxNum();
            // 查询最大人数相等的
            if (maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }
            Long userId = teamQuery.getUserId();
            // 根据创建人来查询
            if (userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }
            // 根据状态来查询
            Integer status = teamQuery.getStatus();
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
            if (statusEnum == null) {
                statusEnum = TeamStatusEnum.PUBLIC;
            }
            //如果不是管理员，并且要查询的房间状态是私密状态，就抛异常
            if (!isAdmin.isAdmin(loginUser) && statusEnum.equals(TeamStatusEnum.PRIVATE)) {
                throw new BusinessException(ErrorCode.NO_AUTH);
            }
            queryWrapper.eq("status", statusEnum.getValue());
        }
        // 不展示已过期的队伍
        // expireTime is null or expireTime > now()
        queryWrapper.and(qw -> qw.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        if (CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
        List<TeamUserVo> teamUserVos = new ArrayList<>();
        //将查询到的team的队长信息进行遍历查询，拿到他们的信息，并组合为teamUser对象进行返回
        for (Team team : teamList) {
            //先将team信息存储到返回对象中
            TeamUserVo teamUserVo = new TeamUserVo();
            BeanUtils.copyProperties(team, teamUserVo);
            //拿到队长id并进行查询
            Long userId = team.getUserId();
            User user = userService.getById(userId);
            UserVo userVo = new UserVo();
            //对用户信息进行脱敏
            BeanUtils.copyProperties(user, userVo);
            teamUserVo.setTeamUser(userVo);
            teamUserVos.add(teamUserVo);
        }
        return teamUserVos;
    }

    @Override
    public boolean updateTeam(TeamUpdateRe teamUpdateRe, User loginUser) {
        Long teamId = teamUpdateRe.getId();
        if (teamId == null || teamId <= 0) return false;
        Team team = this.getById(teamId);
        if (team == null) throw new BusinessException(ErrorCode.SYSTEM_ERROR, "无此队伍");
        Long teamLeaderId = team.getUserId();
        if (!isAdmin.isAdmin(loginUser) && !Objects.equals(loginUser.getId(), teamLeaderId)) {
            throw new BusinessException(ErrorCode.NO_AUTH);
        }
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(team.getStatus());
        if (teamStatusEnum.equals(TeamStatusEnum.PRIVATE) || teamStatusEnum.equals(TeamStatusEnum.SECRET)) {
            if (!StringUtils.hasLength(teamUpdateRe.getPassword()))
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密房间必须设置密码");
        }
        Team updateTeam = new Team();
        BeanUtils.copyProperties(teamUpdateRe, updateTeam);
        return this.updateById(updateTeam);
    }

    @Override
    public boolean joinTeam(TeamJoinRe teamJoinRe, User user) {
        //1. 参数校验
        if (teamJoinRe == null || teamJoinRe.getTeamId() == null || teamJoinRe.getTeamId() <= 0)
            throw new BusinessException(ErrorCode.NULL_ERROR, "加入队伍为空");

        //2. 判断队伍是否存在
        Team team = this.getById(teamJoinRe.getTeamId());
        if (team == null) throw new BusinessException(ErrorCode.NULL_ERROR, "您查询的队伍不存在");

        //3. 判断队伍是否过期
        Date expireTime = team.getExpireTime();
        if (expireTime != null && expireTime.before(new Date()))
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍已过期");
        String lockId = "yu_pao:joinTeam:" + user.getId();
        RLock lock = redissonClient.getLock(lockId);
        try {
            if (lock.tryLock(0,-1, TimeUnit.MILLISECONDS)) {
                //4. 判断队伍加入队伍的数量是否大于五个
                //在team_user中查询数量
                Long loginUser = user.getId();
                QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("userId", loginUser);
                long joinCount = userTeamService.count(queryWrapper);
                if (joinCount >= 5) throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多加入五个队伍");

                //5. 判断是否已经加入过此队伍
                queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("teamId",team.getId());
                queryWrapper.eq("userId",user.getId());
                if (userTeamService.exists(queryWrapper))
                    throw new BusinessException(ErrorCode.PARAMS_ERROR,"已加入此队伍，请勿重复加入");

                //6. 判断队伍人数是否已满
                queryWrapper = new QueryWrapper<>();
                queryWrapper.eq("id",team.getId());
                long joinedCount = userTeamService.count(queryWrapper);
                if (joinedCount == team.getMaxNum()) throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍人数已满");

                //7. 判断队伍是否含有密码，验证密码是否正确
                if (StringUtils.hasLength(team.getPassword())) {
                    if (!team.getPassword().equals(teamJoinRe.getPassword()))
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "房间密码错误");
                }

                //8. 提交数据
                UserTeam userTeam = new UserTeam();
                userTeam.setTeamId(team.getId());
                userTeam.setUserId(user.getId());
                userTeam.setJoinTime(new Date());
                userTeam.setCreateTime(team.getCreateTime());
                userTeam.setUpdateTime(new Date());
                return userTeamService.save(userTeam);
            }
        } catch (InterruptedException e) {
            log.error("doCacheRecommendUser error", e);
        } finally {
            // 只能释放自己的锁
            if (lock.isHeldByCurrentThread()) {
                System.out.println("unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
        return false;
    }

    @Override
    public boolean quit(TeamQuitRe teamQuitRe, User user) {
        //1. 判断参数是否为空
        if (teamQuitRe == null || teamQuitRe.getTeamId() == null || teamQuitRe.getTeamId() <= 0)
            throw new BusinessException(ErrorCode.NULL_ERROR,"参数错误");
        if (user == null) throw new BusinessException(ErrorCode.NOT_LOGIN);
        //2. 查询队伍是否存在
        Team team = this.getById(teamQuitRe.getTeamId());
        if (team == null) throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");

        //3. 判断用户是否在队伍中
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId",teamQuitRe.getTeamId());
        queryWrapper.eq("userId",user.getId());
        if (!userTeamService.exists(queryWrapper))
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"您当前不在队伍中");
        //4. 判断队伍中的人数
        QueryWrapper<UserTeam> teamCountQueryWrapper = new QueryWrapper<>();
        teamCountQueryWrapper.eq("teamId",teamQuitRe.getTeamId());
        long teamPeoCounts = userTeamService.count(teamCountQueryWrapper);
        //如果队伍中只剩一人，那么删除此队伍，并更新user_team
        if (teamPeoCounts == 1) {
            if (!this.removeById(team)) throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除错误");
            if (!userTeamService.remove(teamCountQueryWrapper)) throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除错误");
            else return true;
        }
        //判断此人是否为队长
        if (team.getUserId().equals(user.getId())) {
            QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
            userTeamQueryWrapper.eq("teamId",team.getId());
            userTeamQueryWrapper.last("order by id asc limit 2");
            //拿到前两个人的信息，第一个人就是目前队长（也就是要退出的人）,第二个人就是即将顺位的队长
            List<UserTeam> list = userTeamService.list(userTeamQueryWrapper);
            if (CollectionUtils.isEmpty(list) || list.size() < 2)
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            //拿到顺位队长的信息，并更新数据
            UserTeam userTeamNext = list.get(1);
            long nextTeamUser = userTeamNext.getUserId();
            team.setUserId(nextTeamUser);
            if (!this.updateById(team)) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR);
            }
            return userTeamService.removeById(list.get(0));
        }
        return userTeamService.remove(queryWrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean delete(Long teamId, User user) {
        //1. 校验参数
        if (teamId == null || teamId <= 0) throw new BusinessException(ErrorCode.NULL_ERROR);
        if (user == null) throw new BusinessException(ErrorCode.NOT_LOGIN);
        //2. 查询是否有此队伍
        Team team = this.getById(teamId);
        if (team == null) throw new BusinessException(ErrorCode.SYSTEM_ERROR,"无此队伍");
        //3. 判断是否为队长
        if (!team.getUserId().equals(user.getId())) {
            throw new BusinessException(ErrorCode.NO_AUTH,"暂无权限");
        }
        //4. 删除队伍
        if (!this.removeById(team))
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"队伍删除失败");
        QueryWrapper<UserTeam> queryUserTeam = new QueryWrapper<>();
        queryUserTeam.eq("teamId",teamId);
        boolean ret = userTeamService.remove(queryUserTeam);
        if (!ret) throw new BusinessException(ErrorCode.PARAMS_ERROR,"删除关联表错误");
        return true;
    }

}




